package xyz.scootaloo.mono.security.realm

import org.apache.shiro.authc.AuthenticationException
import org.apache.shiro.authc.AuthenticationInfo
import org.apache.shiro.authc.AuthenticationToken
import org.apache.shiro.authc.SimpleAuthenticationInfo
import org.apache.shiro.authz.AuthorizationInfo
import org.apache.shiro.authz.SimpleAuthorizationInfo
import org.apache.shiro.realm.AuthorizingRealm
import org.apache.shiro.subject.PrincipalCollection
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import xyz.scootaloo.mono.base.lang.R
import xyz.scootaloo.mono.base.lang.SC
import xyz.scootaloo.mono.data.cache.RoleCacheManager
import xyz.scootaloo.mono.security.matcher.DoNotCheckMatcher
import xyz.scootaloo.mono.security.service.AccountService
import xyz.scootaloo.mono.security.token.ClientID
import xyz.scootaloo.mono.security.token.JwtToken
import xyz.scootaloo.mono.security.util.KW_SHIRO

/**
 * 根据 JWT 信息, 检查是否是有效用户, 并赋予用户权限
 *
 * @author flutterdash@qq.com
 * @since 2021/7/25 21:44
 */
@Component("jwtRealm")
class JwtRealm : AuthorizingRealm() {

    @Autowired lateinit var accountService: AccountService

    init {
        credentialsMatcher = DoNotCheckMatcher
    }

    override fun supports(token: AuthenticationToken?): Boolean = token is JwtToken

    /**
     * 验证
     */
    override fun doGetAuthenticationInfo(authcToken: AuthenticationToken?): AuthenticationInfo {
        if (authcToken == null) {
            throw AuthenticationException("认证失败, 在request-header中未找到`${KW_SHIRO.mark}`")
        }

        val jwtToken = authcToken as JwtToken
        val verifyResult = accountService.verifyUserFromJWT(jwtToken.request, jwtToken.token)
        if (verifyResult.success) {
            val client = verifyResult.get()
            return SimpleAuthenticationInfo(client, client.uid, "jwtRealm")
        } else {
            accountService.writeJsonBody(authcToken.response, R.fail(SC.NEED_TO_LOGIN_AGAIN))
            throw AuthenticationException("token无效, 请重新登录")
        }
    }

    /**
     * 授权
     */
    override fun doGetAuthorizationInfo(principals: PrincipalCollection): AuthorizationInfo {
        val client = principals.primaryPrincipal as ClientID
        return SimpleAuthorizationInfo().apply {
            addRoles(RoleCacheManager.list(client.roleCode))
        }
    }

}
